using System;
using System.Collections.Generic;
using System.Text;
using SmartMathLibrary;

namespace SmartMathLibrary.NonlinearEquationSolvers
{
    /// <summary>
    /// This class provides the finding of roots of a function by using a successive 
    /// approximation root finding method. This method should be only used in special
    /// cases. If it is possible, alternative methods should be used.
    /// </summary>
    [Serializable]
    public class SuccessiveApproximationRootFinder : AbstractDerivativeNoNeedRootFinder
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SuccessiveApproximationRootFinder"/> class.
        /// </summary>
        /// <param name="polynomial">The polynomial for finding the roots.</param>
        public SuccessiveApproximationRootFinder(Polynomial polynomial)
            : base(polynomial)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SuccessiveApproximationRootFinder"/> class.
        /// </summary>
        /// <param name="polynomial">The polynomial for finding the roots.</param>
        public SuccessiveApproximationRootFinder(SimplePolynomial polynomial)
            : base(polynomial)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SuccessiveApproximationRootFinder"/> class.
        /// </summary>
        /// <param name="function">The function for finding the roots.</param>
        public SuccessiveApproximationRootFinder(HardRealFunction function)
            : base(function)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SuccessiveApproximationRootFinder"/> class.
        /// </summary>
        /// <param name="function">The function for finding the roots.</param>
        public SuccessiveApproximationRootFinder(IRealFunction function)
            : base(function)
        {
        }

        /// <summary>
        /// Finds one root of the function by using a successive approximation root finding method. The
        /// approximation may has a precision error, so the property PrecisionError has to be
        /// checked.
        /// </summary>
        /// <param name="x">The start value for the calculation.</param>
        /// <returns>A root of the function.</returns>
        public double FindRoots(double x)
        {
            return this.FindRoots(x, 1e-15, 1000);
        }

        /// <summary>
        /// Finds one root of the function by using a successive approximation root finding method. The
        /// approximation may has a precision error, so the property PrecisionError has to be
        /// checked.
        /// </summary>
        /// <param name="x">The start value for the calculation.</param>
        /// <param name="iterations">The maximum of iterations to do, for finding the root.</param>
        /// <returns>A root of the function.</returns>
        public double FindRoots(double x, int iterations)
        {
            return this.FindRoots(x, 1e-15, iterations);
        }

        /// <summary>
        /// Finds one root of the function by using a successive approximation root finding method. The 
        /// approximation may has a precision error, so the property PrecisionError has to be
        /// checked.
        /// </summary>
        /// <param name="x">The start value for the calculation.</param>
        /// <param name="precision">The precision of the result.</param>
        /// <param name="iterations">The maximum of iterations to do, for finding the root.</param>
        /// <returns>A root of the function.</returns>
        public double FindRoots(double x, double precision, int iterations)
        {
            for (int i = 0; i < iterations; i++)
            {
                x = this.Function.SolveAt(x);

                if (Math.Abs(x - this.Function.SolveAt(x)) < precision)
                {
                    this.NeededIterations = i;
                    this.PrecisionError = false;
                    this.RelativeError = Math.Abs(x - this.Function.SolveAt(x));

                    return x;
                }
            }

            this.PrecisionError = true;
            this.NeededIterations = iterations;
            this.RelativeError = Math.Abs(x - this.Function.SolveAt(x));

            return x;
        }
    }
}